今天要介紹第三個資料結構 Map,此 Map 跟 Array.prototype.map 可是完全不一樣的東西! 千萬不要搞錯了。
不同程式語言都有屬於他們的 data collection,例如 Python 有 lists、tuples、dictionaries; Java 有 lists、sets、maps、queues; Ruby 有 hashes 跟 arrays。但 javaScript... 恩,只有 Array。導致 javaScript 工程師在學資料結構時比其他語言工程師難上許多 (平常根本不會碰到阿 !!! )。
好在 ES6 增加了 Set 跟 Map 語法。讓 javaScript 終於可以用更簡潔的語法一窺究竟別種資料結構。
在前一章我們學到元素不會重覆的 Set,今天我們繼續討論另外一種不重複值的資料結構 Map。Set 關心的是元素 { value1, value2 },Map 關心的是 { 鍵(key): 值(value)} 之間的關係。
在查資料時除了 Map 還看到一堆別的專有名詞,但基本上 Map、 dictionaries、associative arrays、hashtable 都是在形容一樣資料結構 (就像北部叫橡皮擦南部叫擦子但明明是一樣東西的概念)。
會發現 key 值什麼型態都可以
// ES6 Map
let myMap= new Map();
var keyString = 'I am string',
keyObj = {},
keyFunc = function() {},
keyNumber = 1
// 增加
myMap.set(keyString , 'string value');
myMap.set(keyObj, {obj: 1});
myMap.set(keyFunc , function(){console.log('I am function')});
myMap.set(keyNumber , 100);
// 有幾個
myMap.size; // 4
// 取值
myMap.get(keyObj); // {obj: 1}
// 看是否存在
myMap.has(keyString ); // true
// 刪掉
myMap.delete(keyNumber);
myMap.size; // 3
// 轉陣列
[...myMap.values()] // ["string value", {obj: 1}, ƒ]
當然你要一開始就把值存進去也可以。
更多 Map 方法可以看 MDN
// have value when define
let myMap = new Map([[keyString ,'string value'], [keyObj, {obj: 1}]]);
之前再介紹 Array 的時候提過,Array 是儲存資料的箱子,而每一個箱子外面都會貼一個 index 標籤。假如你記得西瓜是放在 "0",那就直接去標籤為 0 的箱子拿西瓜 Big O(1)。但如果忘記標籤是甚麼就要每一個箱子都打開檢查 Big O(n)
而 map 像是貼有名字的標籤,一目了然連記都不用記,想拿西瓜,直接打開貼有西瓜標籤的箱子就好。
有人可能會想說那我也可以用 Object 記阿! 是的。但 Map 特別之處就在他的標籤 (key),不像 Object 一定要是 String ,它可以是 Number、Array、Object 或其他任何型別。(下一篇會再詳細比較 Object 跟 Map)
另外雖然 Array 每一個箱子都有貼 index 標籤,但 Array index 基本跟他的 value 沒有任何關係的。
例如我想知道每種水果被吃了幾個
// array 需要兩個陣列來記錄
fruitsArr = ['watermelon', 'grape', 'avocado'] // store friut
ateCount = [0 , 1, 4] // store how many times eat
// 沒有人吃西瓜、葡萄被吃了一個、酪梨被吃 4 個
有 n 個水果,我們要先找 target 水果在不在 fruitsArr 裡,如果有,就在 ateCount 找到相對應 index 然後 +1, 這樣找下來有兩層 loop 時間複雜度是 Big O(n²)
// map
let fruitsMap = new Map([['watermelon',0], ['grape', 1], ['avocado', 4]]);
有 n 個水果,我們要先找 target 水果在不在 fruitsMap 的 Key 裡,有就直接對的 value +1,時間複雜度是 Big O(n),效率比 Array 好很多
自己才剛學 Map 馬上發現它的好處,案子有一個需求是使用者可以勾選任意項目並修改價錢,按儲存後 post API
原本的 get product list API
// data
[
{
"id" : 0,
"product": 'watermelon',
"price": 100
},
{
"id" : 1,
"product": 'apple',
"price": 50
},
{
"id" : 2,
"product": 'banana',
"price": 99
},
...
{
"id" : 50,
"product": 'avocado',
"price": 99
},
]
修改完價錢 change price API 丟的資料要長這樣
// 可以是 multiple 的
[{"id":xx,"price":xx}, {"id":xx,"price":xx}]
我的做法就是當使用者勾選並改價錢就去記錄他的 id 跟 price。當然實際程式碼會判斷更多東西,我只是先簡單列出來
// 存修改哪個商品並把修改後價錢存起來
let tmpChagePrice = new Map();
tmpChagePrice.set(0, {"price": 200})
tmpChagePrice.set(50, {"price": 99})
// 儲存後 post API
let data = [];
tmpChagePrice.forEach( (v, i) => {
data.push({
"id": i,
"price": v.price
})
})
是不是很容易呢!當然假如有更好作法歡迎跟我說 ~~
如有錯誤或需要改進的地方,拜託跟我說。
我會以最快速度修改,感謝您
歡迎追蹤我的部落格,除了技術文也會分享一些在矽谷工作的甘苦。
請問一下, 最後一個範例
let tmpChagePrice = new Map();
用tmpChagePrice 來儲存修改後的價錢
但下方為什麼是用
this.tempList.forEach
將資料放到data內?
文章一開始有說
今天要介紹第三個資料結構 Map,此 Map 跟 Array.prototype.map 可是完全不一樣的東西! 千萬不要搞錯了。
這個 Map 是資料結構的 map,而 Array.prototype.map 是 Array 才能用的方法 Map 是無法用的
那為什麼可以用 forEach,因為資料結構 Array 跟資料結構 Map 都可以用 forEach 這個 method,當然你不想要用 forEach 也是可以用 for..of 啦
誒... 所以 tmpChagePrice
跟 this.tempList
會是一樣的東西嗎?
我懂你的意思了,沒錯是我寫錯了
已經更正了 謝謝你
不客氣